/*
 * Copyright 2002-2007, Marcus Overhagen <marcus@overhagen.de>
 * Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de>
 * Copyright 2013, Haiku, Inc. All Rights Reserved.
 * All rights reserved. Distributed under the terms of the MIT license.
 *
 * Authors:
 *		Stephan Aßmus, superstippi@gmx.de
 *		Marcus Overhagen, marcus@overhagen.de
 */


#include <MediaTrack.h>

#include <new>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <Roster.h>

#include "MediaExtractor.h"
#include "MediaWriter.h"
#include "PluginManager.h"


//#define TRACE_MEDIA_TRACK
#ifdef TRACE_MEDIA_TRACK
#	ifndef TRACE
#		define TRACE printf
#	endif
#else
#	ifndef TRACE
#		define TRACE(a...)
#	endif
#endif

#define ERROR(a...) fprintf(stderr, a)


#define CONVERT_TO_INT32 0
	// TODO: Test! This triggers a few bugs!

// flags used for workarounds
enum {
	FORCE_RAW_AUDIO 				= 0x0001,
	FORCE_RAW_VIDEO 				= 0x0002,
	FORCE_RAW_AUDIO_INT16_FORMAT 	= 0x0010,
	FORCE_RAW_AUDIO_INT32_FORMAT 	= 0x0020,
	FORCE_RAW_AUDIO_FLOAT_FORMAT 	= 0x0040,
	FORCE_RAW_AUDIO_HOST_ENDIAN 	= 0x0100,
	IGNORE_ENCODED_AUDIO 			= 0x1000,
	IGNORE_ENCODED_VIDEO 			= 0x2000,
};

#define B_MEDIA_DISABLE_FORMAT_TRANSLATION 0x4000
	// TODO: move this (after name change?) to MediaDefs.h


class RawDecoderChunkProvider : public ChunkProvider {
public:
							RawDecoderChunkProvider(Decoder* decoder,
								int buffer_size, int frame_size);
	virtual					~RawDecoderChunkProvider();

			status_t		GetNextChunk(const void** chunkBuffer,
								size_t* chunkSize,
								media_header* mediaHeader);

private:
			Decoder*		fDecoder;
			void*			fBuffer;
			int				fBufferSize;
			int				fFrameSize;
};


/*************************************************************
 * protected BMediaTrack
 *************************************************************/

BMediaTrack::~BMediaTrack()
{
	CALLED();

	gPluginManager.DestroyDecoder(fRawDecoder);
	gPluginManager.DestroyDecoder(fDecoder);
	gPluginManager.DestroyEncoder(fEncoder);
}

/*************************************************************
 * public BMediaTrack
 *************************************************************/

status_t
BMediaTrack::InitCheck() const
{
	CALLED();

	return fInitStatus;
}


status_t
BMediaTrack::GetCodecInfo(media_codec_info* _codecInfo) const
{
	CALLED();

	if (fDecoder == NULL)
		return B_NO_INIT;

	*_codecInfo = fCodecInfo;
	strlcpy(_codecInfo->pretty_name, fCodecInfo.pretty_name,
		sizeof(_codecInfo->pretty_name));

	return B_OK;
}


status_t
BMediaTrack::EncodedFormat(media_format* _format) const
{
	CALLED();

	if (_format == NULL)
		return B_BAD_VALUE;

	if (fExtractor == NULL)
		return B_NO_INIT;

	*_format = *fExtractor->EncodedFormat(fStream);

#ifdef TRACE_MEDIA_TRACK
	char s[200];
	string_for_format(*_format, s, sizeof(s));
	printf("BMediaTrack::EncodedFormat: %s\n", s);
#endif

	return B_OK;
}


// for BeOS R5 compatibility
extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(
	BMediaTrack* self, media_format* _format);

status_t DecodedFormat__11BMediaTrackP12media_format(BMediaTrack* self,
	media_format* _format)
{
	return self->DecodedFormat(_format, 0);
}


status_t
BMediaTrack::DecodedFormat(media_format* _format, uint32 flags)
{
	CALLED();

	if (_format == NULL)
		return B_BAD_VALUE;

	if (fExtractor == NULL || fDecoder == NULL)
		return B_NO_INIT;

	gPluginManager.DestroyDecoder(fRawDecoder);
	fRawDecoder = NULL;

#ifdef TRACE_MEDIA_TRACK
	char s[200];
	string_for_format(*_format, s, sizeof(s));
	printf("BMediaTrack::DecodedFormat: req1: %s\n", s);
#endif

	if ((fWorkaroundFlags & FORCE_RAW_AUDIO)
		|| ((fWorkaroundFlags & IGNORE_ENCODED_AUDIO)
			&& _format->type == B_MEDIA_ENCODED_AUDIO)) {
		_format->type = B_MEDIA_RAW_AUDIO;
		_format->u.raw_audio = media_multi_audio_format::wildcard;
	}
	if ((fWorkaroundFlags & FORCE_RAW_VIDEO)
		|| ((fWorkaroundFlags & IGNORE_ENCODED_VIDEO)
			&& _format->type == B_MEDIA_ENCODED_VIDEO)) {
		_format->type = B_MEDIA_RAW_VIDEO;
		_format->u.raw_video = media_raw_video_format::wildcard;
	}
	if (_format->type == B_MEDIA_RAW_AUDIO) {
		if (fWorkaroundFlags & FORCE_RAW_AUDIO_HOST_ENDIAN)
			_format->u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN;

		if (fWorkaroundFlags & FORCE_RAW_AUDIO_INT16_FORMAT) {
			_format->u.raw_audio.format
				= media_raw_audio_format::B_AUDIO_SHORT;
		}
		if (fWorkaroundFlags & FORCE_RAW_AUDIO_INT32_FORMAT) {
			_format->u.raw_audio.format
				= media_raw_audio_format::B_AUDIO_INT;
		}
		if (fWorkaroundFlags & FORCE_RAW_AUDIO_FLOAT_FORMAT) {
			_format->u.raw_audio.format
				= media_raw_audio_format::B_AUDIO_FLOAT;
		}
	}

#ifdef TRACE_MEDIA_TRACK
	string_for_format(*_format, s, sizeof(s));
	printf("BMediaTrack::DecodedFormat: req2: %s\n", s);
#endif

	fFormat = *_format;
	status_t result = fDecoder->NegotiateOutputFormat(_format);

#ifdef TRACE_MEDIA_TRACK
	string_for_format(*_format, s, sizeof(s));
	printf("BMediaTrack::DecodedFormat: nego: %s\n", s);
#endif

	if (_format->type == 0) {
#ifdef TRACE_MEDIA_TRACK
		printf("BMediaTrack::DecodedFormat: Decoder didn't set output format "
			"type.\n");
#endif
	}

	if (_format->type == B_MEDIA_RAW_AUDIO) {
		if (_format->u.raw_audio.byte_order == 0) {
#ifdef TRACE_MEDIA_TRACK
			printf("BMediaTrack::DecodedFormat: Decoder didn't set raw audio "
				"output byte order.\n");
#endif
		}
		if (_format->u.raw_audio.format == 0) {
#ifdef TRACE_MEDIA_TRACK
			printf("BMediaTrack::DecodedFormat: Decoder didn't set raw audio "
				"output sample format.\n");
#endif
		}
		if (_format->u.raw_audio.buffer_size <= 0) {
#ifdef TRACE_MEDIA_TRACK
			printf("BMediaTrack::DecodedFormat: Decoder didn't set raw audio "
				"output buffer size.\n");
#endif
		}
	}

	if (_format->type == B_MEDIA_RAW_VIDEO) {
		if (_format->u.raw_video.display.format == 0) {
#ifdef TRACE_MEDIA_TRACK
			printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
				"output color space.\n");
#endif
		}
		if (_format->u.raw_video.display.line_width == 0) {
#ifdef TRACE_MEDIA_TRACK
			printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
				"output line_width.\n");
#endif
		}
		if (_format->u.raw_video.display.line_count == 0) {
#ifdef TRACE_MEDIA_TRACK
			printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
				"output line_count.\n");
#endif
		}
		if (_format->u.raw_video.display.bytes_per_row == 0) {
#ifdef TRACE_MEDIA_TRACK
			printf("BMediaTrack::DecodedFormat: Decoder didn't set raw video "
				"output bytes_per_row.\n");
#endif
		}
	}

	if ((flags & B_MEDIA_DISABLE_FORMAT_TRANSLATION) == 0) {
		if (fFormat.type == B_MEDIA_RAW_AUDIO
			&& _format->type == B_MEDIA_RAW_AUDIO
			&& fFormat.u.raw_audio.format != 0
			&& fFormat.u.raw_audio.format != _format->u.raw_audio.format) {
			if (SetupFormatTranslation(*_format, &fFormat))
				*_format = fFormat;
		}
	}

	fFormat = *_format;

//	string_for_format(*_format, s, sizeof(s));
//	printf("BMediaTrack::DecodedFormat: status: %s\n", s);
	return result;
}


status_t
BMediaTrack::GetMetaData(BMessage* _data) const
{
	CALLED();

	if (fExtractor == NULL)
		return B_NO_INIT;

	if (_data == NULL)
		return B_BAD_VALUE;

	_data->MakeEmpty();

	return fExtractor->GetStreamMetaData(fStream, _data);
}


int64
BMediaTrack::CountFrames() const
{
	CALLED();

	int64 frames = fExtractor ? fExtractor->CountFrames(fStream) : 0;
//	printf("BMediaTrack::CountFrames: %lld\n", frames);
	return frames;
}


bigtime_t
BMediaTrack::Duration() const
{
	CALLED();

	bigtime_t duration = fExtractor ? fExtractor->Duration(fStream) : 0;
//	printf("BMediaTrack::Duration: %lld\n", duration);
	return duration;
}


int64
BMediaTrack::CurrentFrame() const
{
	return fCurrentFrame;
}


bigtime_t
BMediaTrack::CurrentTime() const
{
	return fCurrentTime;
}

// BMediaTrack::ReadFrames(char*, long long*, media_header*)
// Compatibility for R5 and below. Required by Corum III and Civ:CTP.
#if __GNUC__ < 3

extern "C" status_t
ReadFrames__11BMediaTrackPcPxP12media_header(BMediaTrack* self,
	char* _buffer, int64* _frameCount, media_header* header)
{
	return self->ReadFrames(_buffer, _frameCount, header, 0);
}

#endif	// __GNUC__ < 3

status_t
BMediaTrack::ReadFrames(void* buffer, int64* _frameCount, media_header* header)
{
	return ReadFrames(buffer, _frameCount, header, NULL);
}


status_t
BMediaTrack::ReadFrames(void* buffer, int64* _frameCount,
	media_header* _header, media_decode_info* info)
{
//	CALLED();

	if (fDecoder == NULL)
		return B_NO_INIT;

	if (buffer == NULL || _frameCount == NULL)
		return B_BAD_VALUE;

	media_header header;
	if (_header == NULL)
		_header = &header;

	// Always clear the header first, as the decoder may not set all fields.
	memset(_header, 0, sizeof(media_header));

	status_t result = fRawDecoder != NULL
		? fRawDecoder->Decode(buffer, _frameCount, _header, info)
		: fDecoder->Decode(buffer, _frameCount, _header, info);

	if (result == B_OK) {
		fCurrentFrame += *_frameCount;
		bigtime_t framesDuration = (bigtime_t)(*_frameCount * 1000000
			/ _FrameRate());
		fCurrentTime = _header->start_time + framesDuration;
#if 0
	// This debug output shows drift between calculated fCurrentFrame and
	// time-based current frame, if there is any.
	if (fFormat.type == B_MEDIA_RAW_AUDIO) {
		printf("current frame: %lld / calculated: %lld (%.2f/%.2f)\r",
			fCurrentFrame,
			int64(fCurrentTime * _FrameRate() / 1000000.0 + 0.5),
			fCurrentTime / 1000000.0, (float)fCurrentFrame / _FrameRate());
		fflush(stdout);
	}
#endif
	} else {
		ERROR("BMediaTrack::ReadFrames: decoder returned error %#" B_PRIx32
			" (%s)\n", result, strerror(result));
		*_frameCount = 0;
	}

#if 0
	PRINT(1, "BMediaTrack::ReadFrames: stream %ld, start-time %5Ld.%06Ld, "
		"%lld frames\n", fStream,  _header->start_time / 1000000,
		_header->start_time % 1000000, *out_frameCount);
#endif

	return result;
}


status_t
BMediaTrack::ReplaceFrames(const void* inBuffer, int64* _frameCount,
	const media_header* header)
{
	UNIMPLEMENTED();

	// TODO: Actually, a file is either open for reading or writing at the
	// moment. Since the chunk size of encoded media data will change,
	// implementing this call will only be possible for raw media tracks.

	return B_NOT_SUPPORTED;
}


status_t
BMediaTrack::SeekToTime(bigtime_t* _time, int32 flags)
{
	CALLED();

	if (fDecoder == NULL || fExtractor == NULL)
		return B_NO_INIT;

	if (_time == NULL)
		return B_BAD_VALUE;

	// Make sure flags are valid
	flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_TIME;

	#if DEBUG
	bigtime_t requestedTime = *_time;
	#endif
	int64 frame = 0;

	status_t result = fExtractor->Seek(fStream, flags, &frame, _time);
	if (result != B_OK) {
		ERROR("BMediaTrack::SeekToTime: extractor seek failed\n");
		return result;
	}

	result = fDecoder->SeekedTo(frame, *_time);
	if (result != B_OK) {
		ERROR("BMediaTrack::SeekToTime: decoder seek failed\n");
		return result;
	}

	if (fRawDecoder != NULL) {
		result = fRawDecoder->SeekedTo(frame, *_time);
		if (result != B_OK) {
			ERROR("BMediaTrack::SeekToTime: raw decoder seek failed\n");
			return result;
		}
	}

	fCurrentFrame = frame;
	fCurrentTime = *_time;

	PRINT(1, "BMediaTrack::SeekToTime finished, requested %.6f, result %.6f\n",
		requestedTime / 1000000.0, *_time / 1000000.0);

	return B_OK;
}


status_t
BMediaTrack::SeekToFrame(int64* _frame, int32 flags)
{
	CALLED();

	if (fDecoder == NULL || fExtractor == NULL)
		return B_NO_INIT;

	if (_frame == NULL)
		return B_BAD_VALUE;

	// Make sure flags are valid
	flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_FRAME;

	#if DEBUG
	int64 requestedFrame = *_frame;
	#endif
	bigtime_t time = 0;

	status_t result = fExtractor->Seek(fStream, flags, _frame, &time);
	if (result != B_OK) {
		ERROR("BMediaTrack::SeekToFrame: extractor seek failed\n");
		return result;
	}

	result = fDecoder->SeekedTo(*_frame, time);
	if (result != B_OK) {
		ERROR("BMediaTrack::SeekToFrame: decoder seek failed\n");
		return result;
	}

	if (fRawDecoder != NULL) {
		result = fRawDecoder->SeekedTo(*_frame, time);
		if (result != B_OK) {
			ERROR("BMediaTrack::SeekToFrame: raw decoder seek failed\n");
			return result;
		}
	}

	fCurrentFrame = *_frame;
	fCurrentTime = time;

	PRINT(1, "BMediaTrack::SeekToTime SeekToFrame, requested %lld, "
		"result %lld\n", requestedFrame, *_frame);

	return B_OK;
}


status_t
BMediaTrack::FindKeyFrameForTime(bigtime_t* _time, int32 flags) const
{
	CALLED();

	if (fExtractor == NULL)
		return B_NO_INIT;

	if (_time == NULL)
		return B_BAD_VALUE;

	// Make sure flags are valid
	flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_TIME;

	int64 frame = 0;
		// dummy frame, will be ignored because of flags
	status_t result = fExtractor->FindKeyFrame(fStream, flags, &frame, _time);
	if (result != B_OK) {
		ERROR("BMediaTrack::FindKeyFrameForTime: extractor seek failed: %s\n",
			strerror(result));
	}

	return result;
}


status_t
BMediaTrack::FindKeyFrameForFrame(int64* _frame, int32 flags) const
{
	CALLED();

	if (fExtractor == NULL)
		return B_NO_INIT;

	if (_frame == NULL)
		return B_BAD_VALUE;

	// Make sure flags are valid
	flags = (flags & B_MEDIA_SEEK_DIRECTION_MASK) | B_MEDIA_SEEK_TO_FRAME;

	bigtime_t time = 0;
		// dummy time, will be ignored because of flags
	status_t result = fExtractor->FindKeyFrame(fStream, flags, _frame, &time);
	if (result != B_OK) {
		ERROR("BMediaTrack::FindKeyFrameForFrame: extractor seek failed: %s\n",
			strerror(result));
	}

	return result;
}


status_t
BMediaTrack::ReadChunk(char** _buffer, int32* _size, media_header* _header)
{
	CALLED();

	if (fExtractor == NULL)
		return B_NO_INIT;

	if (_buffer == NULL || _size == NULL)
		return B_BAD_VALUE;

	media_header header;
	if (_header == NULL)
		_header = &header;

	// Always clear the header first, as the extractor may not set all fields.
	memset(_header, 0, sizeof(media_header));

	const void* buffer;
	size_t size;
	status_t result = fExtractor->GetNextChunk(fStream, &buffer, &size,
		_header);

	if (result == B_OK) {
		*_buffer = const_cast<char*>(static_cast<const char*>(buffer));
			// TODO: Change the pointer type when we break the API.
		*_size = size;
		// TODO: This changes the meaning of fCurrentTime from pointing
		// to the next chunk start time (i.e. after seeking) to the start time
		// of the last chunk. Asking the extractor for the current time will
		// not work so well because of the chunk cache. But providing a
		// "duration" field in the media_header could be useful.
		fCurrentTime = _header->start_time;
		fCurrentFrame = (int64)(fCurrentTime * _FrameRate() / 1000000LL);

	}

	return result;
}


status_t
BMediaTrack::AddCopyright(const char* copyright)
{
	if (fWriter == NULL)
		return B_NO_INIT;

	return fWriter->SetCopyright(fStream, copyright);
}


status_t
BMediaTrack::AddTrackInfo(uint32 code, const void* data, size_t size,
	uint32 flags)
{
	if (fWriter == NULL)
		return B_NO_INIT;

	return fWriter->AddTrackInfo(fStream, code, data, size, flags);
}


status_t
BMediaTrack::WriteFrames(const void* data, int32 frameCount, int32 flags)
{
	media_encode_info encodeInfo;
	encodeInfo.flags = flags;

	return WriteFrames(data, frameCount, &encodeInfo);
}


status_t
BMediaTrack::WriteFrames(const void* data, int64 frameCount,
	media_encode_info* info)
{
	if (fEncoder == NULL)
		return B_NO_INIT;

	return fEncoder->Encode(data, frameCount, info);
}


status_t
BMediaTrack::WriteChunk(const void* data, size_t size, uint32 flags)
{
	media_encode_info encodeInfo;
	encodeInfo.flags = flags;

	return WriteChunk(data, size, &encodeInfo);
}


status_t
BMediaTrack::WriteChunk(const void* data, size_t size, media_encode_info* info)
{
	if (fWriter == NULL)
		return B_NO_INIT;

	return fWriter->WriteChunk(fStream, data, size, info);
}


status_t
BMediaTrack::Flush()
{
	if (fWriter == NULL)
		return B_NO_INIT;

	return fWriter->Flush();
}


// deprecated BeOS R5 API
BParameterWeb*
BMediaTrack::Web()
{
	BParameterWeb* web;
	if (GetParameterWeb(&web) == B_OK)
		return web;

	return NULL;
}


status_t
BMediaTrack::GetParameterWeb(BParameterWeb** outWeb)
{
	if (outWeb == NULL)
		return B_BAD_VALUE;

	if (fEncoder == NULL)
		return B_NO_INIT;

	// TODO: This method is new in Haiku. The header mentions it returns a
	// copy. But how could it even do that? How can one clone a web and make
	// it point to the same BControllable?
	*outWeb = fEncoder->ParameterWeb();
	if (*outWeb != NULL)
		return B_OK;

	return B_NOT_SUPPORTED;
}


status_t
BMediaTrack::GetParameterValue(int32 id, void* value, size_t* size)
{
	if (value == NULL || size == NULL)
		return B_BAD_VALUE;

	if (fEncoder == NULL)
		return B_NO_INIT;

	return fEncoder->GetParameterValue(id, value, size);
}


status_t
BMediaTrack::SetParameterValue(int32 id, const void* value, size_t size)
{
	if (value == NULL || size == 0)
		return B_BAD_VALUE;

	if (fEncoder == NULL)
		return B_NO_INIT;

	return fEncoder->SetParameterValue(id, value, size);
}


BView*
BMediaTrack::GetParameterView()
{
	if (fEncoder == NULL)
		return NULL;

	return fEncoder->ParameterView();
}


status_t
BMediaTrack::GetQuality(float* quality)
{
	if (quality == NULL)
		return B_BAD_VALUE;

	encode_parameters parameters;
	status_t result = GetEncodeParameters(&parameters);
	if (result != B_OK)
		return result;

	*quality = parameters.quality;

	return B_OK;
}


status_t
BMediaTrack::SetQuality(float quality)
{
	encode_parameters parameters;
	status_t result = GetEncodeParameters(&parameters);
	if (result != B_OK)
		return result;

	if (quality < 0.0f)
		quality = 0.0f;

	if (quality > 1.0f)
		quality = 1.0f;

	parameters.quality = quality;

	return SetEncodeParameters(&parameters);
}


status_t
BMediaTrack::GetEncodeParameters(encode_parameters* parameters) const
{
	if (parameters == NULL)
		return B_BAD_VALUE;

	if (fEncoder == NULL)
		return B_NO_INIT;

	return fEncoder->GetEncodeParameters(parameters);
}


status_t
BMediaTrack::SetEncodeParameters(encode_parameters* parameters)
{
	if (parameters == NULL)
		return B_BAD_VALUE;

	if (fEncoder == NULL)
		return B_NO_INIT;

	return fEncoder->SetEncodeParameters(parameters);
}


status_t
BMediaTrack::Perform(int32 selector, void* data)
{
	return B_OK;
}

// #pragma mark - private


BMediaTrack::BMediaTrack(BPrivate::media::MediaExtractor* extractor,
	int32 stream)
{
	CALLED();

	fWorkaroundFlags = 0;
	fDecoder = NULL;
	fRawDecoder = NULL;
	fExtractor = extractor;
	fStream = stream;
	fInitStatus = B_OK;

	SetupWorkaround();

	status_t ret = fExtractor->CreateDecoder(fStream, &fDecoder, &fCodecInfo);
	if (ret != B_OK) {
		TRACE("BMediaTrack::BMediaTrack: Error: creating decoder failed: "
			"%s\n", strerror(ret));
		// We do not set fInitStatus here, because ReadChunk should still work.
		fDecoder = NULL;
	}

	fCurrentFrame = 0;
	fCurrentTime = 0;

	// not used:
	fEncoder = NULL;
	fEncoderID = 0;
	fWriter = NULL;
}


BMediaTrack::BMediaTrack(BPrivate::media::MediaWriter* writer,
	int32 streamIndex, media_format* format,
	const media_codec_info* codecInfo)
{
	CALLED();

	fWorkaroundFlags = 0;
	fEncoder = NULL;
	fEncoderID = -1;
		// TODO: Not yet sure what this was needed for...
	fWriter = writer;
	fStream = streamIndex;
	fInitStatus = B_OK;

	SetupWorkaround();

	if (codecInfo != NULL) {
		status_t ret = fWriter->CreateEncoder(&fEncoder, codecInfo, format);
		if (ret != B_OK) {
			TRACE("BMediaTrack::BMediaTrack: Error: creating decoder failed: "
				"%s\n", strerror(ret));
			// We do not set fInitStatus here, because WriteChunk should still
			// work.
			fEncoder = NULL;
		} else {
			fCodecInfo = *codecInfo;
			fInitStatus = fEncoder->SetUp(format);
		}
	}

	fFormat = *format;

	// not used:
	fCurrentFrame = 0;
	fCurrentTime = 0;
	fDecoder = NULL;
	fRawDecoder = NULL;
	fExtractor = NULL;
}


// Does nothing, returns B_ERROR, for Zeta compatiblity only
status_t
BMediaTrack::ControlCodec(int32 selector, void* io_data, size_t size)
{
	return B_ERROR;
}


void
BMediaTrack::SetupWorkaround()
{
	app_info	ainfo;
	thread_info	tinfo;

	get_thread_info(find_thread(0), &tinfo);
	be_roster->GetRunningAppInfo(tinfo.team, &ainfo);

	if (strcmp(ainfo.signature, "application/x-vnd.marcone-soundplay") == 0) {
		fWorkaroundFlags = FORCE_RAW_AUDIO | FORCE_RAW_AUDIO_INT16_FORMAT
			| FORCE_RAW_AUDIO_HOST_ENDIAN;
		printf("BMediaTrack::SetupWorkaround: SoundPlay workaround active\n");
	}
	if (strcmp(ainfo.signature, "application/x-vnd.Be.MediaPlayer") == 0) {
		fWorkaroundFlags = IGNORE_ENCODED_AUDIO | IGNORE_ENCODED_VIDEO;
		printf("BMediaTrack::SetupWorkaround: MediaPlayer workaround active\n");
	}

#if CONVERT_TO_INT32
	// TODO: Test
	if (!(fWorkaroundFlags & FORCE_RAW_AUDIO_INT16_FORMAT))
		fWorkaroundFlags |= FORCE_RAW_AUDIO_INT32_FORMAT;
#endif
}


bool
BMediaTrack::SetupFormatTranslation(const media_format &from, media_format* to)
{
	gPluginManager.DestroyDecoder(fRawDecoder);
	fRawDecoder = NULL;

#ifdef TRACE_MEDIA_TRACK
	char s[200];
	string_for_format(from, s, sizeof(s));
	printf("BMediaTrack::SetupFormatTranslation: from: %s\n", s);
#endif

	status_t result = gPluginManager.CreateDecoder(&fRawDecoder, from);
	if (result != B_OK) {
		ERROR("BMediaTrack::SetupFormatTranslation: CreateDecoder failed\n");
		return false;
	}

	// XXX video?
	int buffer_size = from.u.raw_audio.buffer_size;
	int frame_size = (from.u.raw_audio.format & 15)
		* from.u.raw_audio.channel_count;
	media_format fromNotConst = from;

	ChunkProvider* chunkProvider
		= new (std::nothrow) RawDecoderChunkProvider(fDecoder, buffer_size,
			frame_size);
	if (chunkProvider == NULL) {
		ERROR("BMediaTrack::SetupFormatTranslation: can't create chunk "
			"provider\n");
		goto error;
	}
	fRawDecoder->SetChunkProvider(chunkProvider);

	result = fRawDecoder->Setup(&fromNotConst, 0, 0);
	if (result != B_OK) {
		ERROR("BMediaTrack::SetupFormatTranslation: Setup failed\n");
		goto error;
	}

#ifdef TRACE_MEDIA_TRACK
	string_for_format(*to, s, sizeof(s));
	printf("BMediaTrack::SetupFormatTranslation:   to: %s\n", s);
#endif

	result = fRawDecoder->NegotiateOutputFormat(to);
	if (result != B_OK) {
		ERROR("BMediaTrack::SetupFormatTranslation: NegotiateOutputFormat "
			"failed\n");
		goto error;
	}

#ifdef TRACE_MEDIA_TRACK
	string_for_format(*to, s, sizeof(s));
	printf("BMediaTrack::SetupFormatTranslation:  result: %s\n", s);
#endif

	return true;

error:
	gPluginManager.DestroyDecoder(fRawDecoder);
	fRawDecoder = NULL;
	return false;
}


double
BMediaTrack::_FrameRate() const
{
	switch (fFormat.type) {
		case B_MEDIA_RAW_VIDEO:
			return fFormat.u.raw_video.field_rate;
		case B_MEDIA_ENCODED_VIDEO:
			return fFormat.u.encoded_video.output.field_rate;
		case B_MEDIA_RAW_AUDIO:
			return fFormat.u.raw_audio.frame_rate;
		case B_MEDIA_ENCODED_AUDIO:
			return fFormat.u.encoded_audio.output.frame_rate;
		default:
			return 1.0;
	}
}

#if 0
// unimplemented
BMediaTrack::BMediaTrack()
BMediaTrack::BMediaTrack(const BMediaTrack &)
BMediaTrack &BMediaTrack::operator=(const BMediaTrack &)
#endif

status_t BMediaTrack::_Reserved_BMediaTrack_0(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_1(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_2(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_3(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_4(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_5(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_6(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_7(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_8(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_9(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_10(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_11(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_12(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_13(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_14(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_15(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_16(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_17(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_18(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_19(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_20(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_21(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_22(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_23(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_24(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_25(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_26(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_27(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_28(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_29(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_30(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_31(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_32(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_33(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_34(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_35(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_36(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_37(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_38(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_39(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_40(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_41(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_42(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_43(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_44(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_45(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_46(int32 arg, ...) { return B_ERROR; }
status_t BMediaTrack::_Reserved_BMediaTrack_47(int32 arg, ...) { return B_ERROR; }


RawDecoderChunkProvider::RawDecoderChunkProvider(Decoder* decoder,
	int buffer_size, int frame_size)
{
//	printf("RawDecoderChunkProvider: buffer_size %d, frame_size %d\n",
//		buffer_size, frame_size);
	fDecoder = decoder;
	fFrameSize = frame_size;
	fBufferSize = buffer_size;
	fBuffer = malloc(buffer_size);
}


RawDecoderChunkProvider::~RawDecoderChunkProvider()
{
	free(fBuffer);
}


status_t
RawDecoderChunkProvider::GetNextChunk(const void** chunkBuffer,
	size_t* chunkSize, media_header* header)
{
	int64 frames;
	media_decode_info info;
	status_t result = fDecoder->Decode(fBuffer, &frames, header, &info);
	if (result == B_OK) {
		*chunkBuffer = fBuffer;
		*chunkSize = frames * fFrameSize;
//		printf("RawDecoderChunkProvider::GetNextChunk, %lld frames, "
//			"%ld bytes, start-time %lld\n", frames, *chunkSize,
//			header->start_time);
	} else
		ERROR("RawDecoderChunkProvider::GetNextChunk failed\n");

	return result;
}
